iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 10
0
自我挑戰組

軟體開發隨筆雜記--試著解決問題系列 第 10

如何做一個簡易的視訊處理工具(1)

  • 分享至 

  • xImage
  •  

這邊是一個視訊撥放器範例
https://ithelp.ithome.com.tw/upload/images/20200925/20119608qXZpXyDDrn.jpg

在這邊我先簡單定義一個視訊撥放面板,

def init_frameviewer(self):

定義常和寬

    self.LABEL_WIDTH = 540
    self.LABEL_HEIGHT = 330

影片來源控制面板

    self.StreamCtrlPanel = tk.LabelFrame(self.parent ,
                                         text="Stream Control Panel",
                                         font=('Courier', 9))
    self.StreamCtrlPanel.pack(side=tk.TOP, expand=tk.NO, fill = tk.X)

選擇local camera的單選紐

    self.selStr = tk.StringVar()
    fromlocalCam = tk.Radiobutton(self.StreamCtrlPanel, 
                                  text = "Local Camera",
                                  font=('Courier', 9), 
                                  variable = self.selStr, 
                                  value = "From Local Camera",
                                  command = self.init_VideoCapture)
    fromlocalCam.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)

輸入其他裝置來源網址的變數

    self.DEV_VIDEO_NAME= tk.StringVar()
    self.DEV_VIDEO_NAME.set("視訊網址")
    

選擇其他裝置視訊來源的單選紐

    fromdevCam = tk.Radiobutton(self.StreamCtrlPanel,
                                text = "Device Camera",
                                font=('Courier', 9), 
                                variable = self.selStr,
                                value = "From Device Camera", 
                                command = self.init_VideoCapture)
    fromdevCam.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)
    

輸入其他裝置來源的輸入方塊

    self.DeviceVideoPath= tk.Entry(self.StreamCtrlPanel , 
                                   textvariable=self.DEV_VIDEO_NAME, 
                                   width = 10)
    self.DeviceVideoPath.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)        
    

選擇local端的視訊影片

    fromlocalVideofile = tk.Radiobutton(self.StreamCtrlPanel, 
                                        text = "Local Video File",
                                        font=('Courier', 9), 
                                        variable = self.selStr,
                                        value = "From Local Video File", 
                                        command = self.load_video)
    fromlocalVideofile.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)

輸入local端影片變數

    self.LOCAL_VIDEO_NAME = tk.StringVar()
    

輸入local端影片來源輸入方塊

    self.LocalVideoPath= tk.Entry(self.StreamCtrlPanel , 
                                  textvariable=self.LOCAL_VIDEO_NAME, 
                                  width = 10)
    self.LocalVideoPath.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)
    

影片操作面板

    self.VideoCtrlPanel = tk.LabelFrame(self.parent  , 
                                        text="VideoControl Panel",
                                        font=('Courier', 9))
    self.VideoCtrlPanel.pack(side=tk.TOP, expand=tk.NO, fill = tk.X)

播放鍵

    showframebutton = tk.Button(self.VideoCtrlPanel, 
                                text = "PLAY",
                                font=('Courier', 9), 
                                command = self.show_frame)
    showframebutton.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)
    

停止鍵

    capreleabutton = tk.Button(self.VideoCtrlPanel, 
                               text ="STOP",
                               font=('Courier', 9),
                               command = self.cap_release)
    capreleabutton.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)

把影片分割成一張張frame的功能鍵

    split_framebutton = tk.Button(self.VideoCtrlPanel, 
                                  text ="Split",
                                  font=('Courier', 9), 
                                  command = self.split_frame)
    split_framebutton.pack(side=tk.LEFT, expand=tk.NO, fill = tk.X)

承載撥放影片的frame版面

    self.streamplay_tab = tk.Frame(self.parent)
    self.streamplay_tab.pack(side = tk.LEFT, expand=tk.YES, fill=tk.BOTH)

定義self.stream為承載影片的載體,使用物件label

    self.stream = tk.Label(self.streamplay_tab, 
                           width=self.LABEL_WIDTH, 
                           height=self.LABEL_HEIGHT)
    self.stream.pack(side=tk.LEFT, expand=tk.NO, fill = tk.BOTH)
   

撥放影片

 def show_frame(self, event = None):
    _, frame = self.cap.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    frame = cv2.resize(frame,(self.LABEL_WIDTH,self.LABEL_HEIGHT))
    img = Image.fromarray(frame)
    imgtk = ImageTk.PhotoImage(image=img)
    self.stream.imgtk = imgtk
    self.stream.configure(image=imgtk)
    self.stream.after(15, self.show_frame)

停止視訊

def cap_release(self, event = None):
    self.cap.release()
    self.stream.configure(image=圖片檔路徑)

切分視訊成為frame

def split_frame(self, event = None):
    if self.selStr.get() =="From Local Camera":
        self.cap = cv2.VideoCapture(0)
    elif self.selStr.get() =="From Local Video File":
        self.cap = cv2.VideoCapture(self.localvideoname)
    #調整預設影像大小,預設值很大,很吃效能 
    self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.LABEL_WIDTH)
    self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.LABEL_HEIGHT)

    if not os.path.exists(os.getcwd()+"/split_frame"):
        os.mkdir("split_frame")
    while(self.cap.isOpened()): #當攝影機打開時,對每個frame進行偵測    
        T = int(100*time.time())
        #讀出frame資訊
        ret, frame = self.cap.read()
        #輸出到畫面
        cv2.imshow("Split frame", frame)
        cv2.imwrite('split_frame/'+str(T)+'.jpg', frame)
        #如果按下ESC键,就退出
        if cv2.waitKey(1) == 27: break
    #釋放記憶體
    self.cap.release()
    #關閉所有視窗
    cv2.destroyAllWindows() 

選擇視訊來源

def init_VideoCapture(self, event = None):
    if self.selStr.get() =="From Local Camera":
        self.cap = cv2.VideoCapture(0)

    elif self.selStr.get() =="From Local Video File":
        self.cap = cv2.VideoCapture(self.localvideoname)
   
    elif self.selStr.get() =="From Device Camera":
        self.cap = cv2.VideoCapture(self.DEV_VIDEO_NAME.get())

以下是選取視訊來源程序上的差異:

https://ithelp.ithome.com.tw/upload/images/20200927/201196086vEvVAVDbC.jpg

打開資料夾載入視訊檔

def load_video(self, event = None):
    self.localvideoname =  filedialog.askopenfilename(
                                      initialdir = "/",
                                      title = "Select file",
                                      filetypes = (("mp4 files","*.mp4*"),
                                                   ("all files","*.*")))
    self.LOCAL_VIDEO_NAME.set(self.localvideoname)
    self.init_VideoCapture()

在製作視訊工具時,我想在畫面上打映出時間戳記,在show_frame()內部可以利用下面的程式碼...

    # timestamp
    Time = time.time()
    # datetime 物件
    local_time = time.ctime(Time)
    # write the time information on the frame
    cv2.putText(frame, str(local_time), (10, 10), 
                1,
                1, 
                (255,255,255), 
                1,
                0) 

上一篇
如何做一個簡易相簿(2)
下一篇
如何做一個簡易的視訊處理工具(2)
系列文
軟體開發隨筆雜記--試著解決問題33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言